///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.controls
{
  import ilog.gauges.circular.CircularCategoryScale;
  import ilog.gauges.gaugesClasses.circular.SingleCircularGauge;
  import ilog.utils.CSSUtil;
  
  import mx.styles.CSSStyleDeclaration;
  import ilog.gauges.gaugesClasses.circular.CircularValueRenderer;

  /**
   * Controls the visibility of the scale ticks.
   *  
   * @default true
   */
  [Style(name="showMajorTicks", type="Boolean", inherit="no")]
 
  /**
   * A style name to be used for the tick renderers. Useful
   * for tick renderers that might use properties from this
   * style name.
   * 
   * @default null
   * @since ILOG Elixir 2.0
   */
  [Style(name="majorTickStyleName", type="String", inherit="no")]
 
  /**
   * This is a base class for predefined knobs.
   * This class defines a knob with the following elements:
   * <ul>
   *   <li>a scale (angle range and categories are customizable)</li>
   *   <li>a value renderer (typically, a needle)</li> 
   *   <li>a title label</li>
   * </ul>
   * 
   * A <code>Knob</code> is editable by default, contrary to its base
   * class.
   * 
   * <p>When a given property is available on <code>Knob</code>,
   * it overrides any corresponding property that may have been set on one of its predefined
   * elements. For example, the <code>categories</code> property value of the 
   * <code>Knob</code> overrides the <code>categories</code>
   * property value of the <code>CircularCategoryScale</code> referenced
   * by the <code>scale</code> property.</p>
   *   
   * <p>You do not use the <code>Knob</code> class 
   * directly, even if it is possible. Instead, you use one of its subclasses, such as 
   * <code>SimpleKnob</code>, or you create your own subclass.</p>
   *  
   * @mxml
   *  
   * <p>The <code>&lt;ilog:Knob&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;ilog:Knob
   *   <strong>Properties</strong>
   *   categories="[]"
   *   startAngle="210"
   *   endAngle="330"
   *   orientation="clockwise|cclockwise"
   *   value="<i>String, null</i>"
   *   scale="<i>A default category scale</i>"
   * 
   *   <strong>Styles</strong>
   *   showMajorTicks="true|false"
   *   majorTickStyleName="null"
   * /&gt;
   * </pre>
   */  
  public class Knob extends SingleCircularGauge
  {
    
    /**
     * @private 
     */     
    private static var stylesInited:Boolean = initStyles(); 

    /**
     * @private 
     */ 
    private static function initStyles():Boolean
    {
      var style:CSSStyleDeclaration =
        CSSUtil.createSelector("Knob");   
            
      style.defaultFactory = function():void {
        this.showMajorTicks = true;
        this.showTrack = false;
        this.majorTickStyleName = null;
      }
      return true;
    }

    private var _elementsDirty:Boolean = false;

    private var _scaleParam:Object = {
      startAngle: 210,
      endAngle: 330,
      orientation : "clockwise",
      categories : [],
      dirty: false
    };    
    
    /**
     * Constructor 
     */ 
    public function Knob()
    {
      super();
      scale = new CircularCategoryScale();
      editable = true;
    }
    
    [Bindable]
    [Inspectable(category="Gauge")]
        
    /**
     * The logical scale of the gauge.
     * 
    * @default ilog.gauges.circular.CircularCategoryScale 
     */ 
    public function get scale():CircularCategoryScale
    {
      return scales.length > 0 ? scales[0] : null;
    }
    
    /**
     * @private 
     */     
    public function set scale(value:CircularCategoryScale):void 
    {
      scales = [value];
    }
    
    private var _categories:Array;
        
    [Inspectable(category="Gauge")]
    
    /**
     * The categories of the knob. Each category must be unique.
     * 
     * 
     * @default []
     */       
    public function get categories():Array
    {
      return (scale != null) ? scale.categories : _categories;
    }

    /**
     * @private 
     */   
    public function set categories(value:Array):void
    {
      _categories = value;
      _scaleParam.categories = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }

    private var _value:String = null;
    
    [Bindable("change")]
    [Bindable("valueCommit")]
    [Inspectable(category="Gauge")]
        
    /**
     * The current value of the gauge, pointed by the value renderer. 
     * 
     * @default null
     */       
    public function get value():String
    {
      return (valueRenderer != null) ? valueRenderer.value as String : _value;
    }

    /**
     * @private 
     */   
    public function set value(val:String):void
    {
      _value = val;
      if (valueRenderer != null) {
        valueRenderer.value = val;
      }
    }
    
    /**
     * @private 
     */     
    public override function set valueRenderer(renderer:CircularValueRenderer):void
    {
      // store latest value in internal buffer
      _value = value;
      super.valueRenderer = renderer;
      if (valueRenderer != null) {
        // make sure the old value is put back on the new renderer, without animation
        // for now
        var duration:Number = valueRenderer.getStyle("animationDuration");
        if (!isNaN(duration) && duration != 0) 
          valueRenderer.setStyle("animationDuration", 0);
        valueRenderer.value = _value;
        if (!isNaN(duration) && duration != 0)      
          valueRenderer.setStyle("animationDuration", duration);
      }                
    }       
    
    [Inspectable(category="Gauge", defaultValue="210")]

    /**
     * The start angle of the scale, in degrees.
     * 
     * @default 210 
     */       
    public function get startAngle():Number
    {
      return (scale != null) ? scale.startAngle : _scaleParam.startAngle;
    }

    /**
     * @private 
     */
    public function set startAngle(value:Number):void
    {
      _scaleParam.startAngle = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }
    
    [Inspectable(category="Gauge", defaultValue="330")] 
       
    /**
     * The end angle of the scale, in degrees.
     * 
     * @default 330
     */ 
    public function get endAngle():Number
    {
      return (scale != null) ? scale.endAngle : _scaleParam.endAngle;
    }

    /**
     * @private 
     */
    public function set endAngle(value:Number):void
    {
      _scaleParam.endAngle = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }

    [Inspectable(category="Gauge", enumeration="clockwise,cclockwise")]
    
    /**
     * The orientation of the scale. It can be clockwise (<code>"clockwise"</code>)
     * or counterclockwise (<code>"cclockwise"</code>). 
     * 
     * @default "clockwise"
     */  
    public function get orientation():String
    {
      return (scale != null) ? scale.orientation : _scaleParam.orientation;
    }

    /**
     * @private 
     */
    public function set orientation(value:String):void
    {
      _scaleParam.orientation = value;
      _scaleParam.dirty = true;
      invalidateProperties();
    }
     
    /**
     * @inheritDoc
     */     
    override protected function layoutElements():void
    {
      super.layoutElements();
    } 
    
    /**
     * @private
     */
    protected override function commitProperties():void
    {
      if (_scaleParam.dirty && scale != null /*&& getStyle("showScale") == true*/) {
        scale.orientation = _scaleParam.orientation;
        scale.startAngle = _scaleParam.startAngle;
        scale.endAngle = _scaleParam.endAngle;
        scale.categories = _scaleParam.categories;
        _scaleParam.dirty = false;
        invalidateDisplayList();
      }      
      super.commitProperties();
    }
  }
}
